/*
 * yauosk - Yet Another Useless Operating System Kernel
 *
 * Copyright (c) 2009-2010 Matteo Cicuttin
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <arch/x86/machine.h>
#include <systm.h>

/* Interrupt Descriptor Table */
static struct gatedesc idt[256];
/* Trap handler function pointers */
static void (*traphandler[256])(void);

/* Needed to redirect execution to alltraps */
extern uint vectors[];


void
trap_idt_init()
{
	load_idt(idt, sizeof(idt));
}

/*
 * Initialize trap handling. Build an interrupt descriptor table and load it
 */

void
trap_init(void)
{
	int i;
	for(i = 0; i < 256; i++)
	{
		SETGATE(idt[i], INTR_GATE, GDT_KCODE, vectors[i], DPL_KERNEL);
		traphandler[i] = NULL;
	}
	
	SETGATE(idt[TRAP_SYSCALL], INTR_GATE, GDT_KCODE, vectors[TRAP_SYSCALL], DPL_USER);
	trap_idt_init();
	irq_8259_init();
	
	printf("Interrupt handling OK\n");
}

/*
 * Install/remove trap handlers. Trap handlers are called by trap()
 */

void
trap_install_handler(trap_t trap, void (*h)(void))
{
	if (traphandler[trap])
	{
		printf("Trap %d is already handled, check your code\n", trap);
		return;
	}
	traphandler[trap] = h;
}

void
trap_remove_handler(trap_t trap)
{
	traphandler[trap] = NULL;
}

/*
 * Install/remove interrupt handlers. Interrupt handlers are called by trap()
 */

void
irq_install_handler(irq_t irq, void (*h)(void))
{
	if (traphandler[irq+IRQ_OFFSET])
	{
		printf("IRQ %d is already handled, check your code\n", irq);
		return;
	}
	traphandler[irq+IRQ_OFFSET] = h;
}

void
irq_remove_handler(irq_t irq)
{
	traphandler[irq+IRQ_OFFSET] = NULL;
}

void
dump_trap_frame(struct trapframe *tf)
{
	printf(" * * * TRAPFRAME DUMP BEGIN (TRAP %d) %p* * *\n", tf->trapno, tf);
	printf("EAX: %x EBX: %x ECX: %x EDX: %x\n", tf->eax, tf->ebx, tf->ecx, tf->edx);
	printf("ESI: %x EDI: %x EBP: %x _ESP: %x\n", tf->esi, tf->edi, tf->ebp, tf->_esp);
	printf("CS: %x DS: %x ES: %x SS: %x\n", tf->cs, tf->ds, tf->es, tf->ss);
	printf("EIP: %x EFLAGS: %x ERR: %x ESP: %x\n", tf->eip, tf->eflags, tf->err, tf->esp);
	printf(" * * * TRAPFRAME DUMP END (TRAP %d) * * *\n", tf->trapno);
	x86_dump_registers();
}

/*
 * x86 trap handler. When an interrupt arrives the processor executes this
 * function, that is called by alltraps in trapasm.S
 */

void
trap(struct trapframe *tf)
{
	//x86_dump_registers();

	dump_trap_frame(tf);
	
	/*printf("Trap handler to call: %p\n", traphandler[tf->trapno]);*/
	//printf("Trap %d on CPU %d. EIP = %x\n", tf->trapno, thiscpu(), tf->eip);
	
	if ( !(*traphandler[tf->trapno]) )
	{
		printf("Unhandled exception %d on cpu %d\n", tf->trapno, thiscpu());
		//lapic_eoi(); /* Don't acknowledge an interrupt that isn't handled */
		return;
	}

	(*traphandler[tf->trapno])();
	lapic_eoi();
}
